/*
	ZPM Device for GarrysMod10
	Copyright (C) 2008 JDM12989

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
--################# HEADER #################
AddCSLuaFile("cl_init.lua");
AddCSLuaFile("shared.lua");
include("shared.lua");
--################# SENT CODE ###############

--################## Init @JDM12989
function ENT:Initialize()
	self:SetModel("models/mak/console/console.mdl");
	self:PhysicsInit(SOLID_VPHYSICS);
	self:SetMoveType(MOVETYPE_VPHYSICS);
	self:SetSolid(SOLID_VPHYSICS);
	self.enabled = false;
	self.overload = false;
	self.buffer = 0;
	self.buffer_max = 16000000; -- 16 mil
	self.zpm_slot = {nil,nil,nil};
	self.slot_cent = {Vector(10,0,-16.5),Vector(10,14.2,8),Vector(10,-14.2,8)};
	self.radius = StarGate.CFG:Get("zpm_device","radius",3000);
	self.damage = StarGate.CFG:Get("zpm_device","damage",2000);
	self.alarm = CreateSound(self,"SGA_selfdestruct-alarm.wav");
	self:AddResource("energy",StarGate.CFG:Get("zpm_device","energy_capacity",35000));
	self:AddResource("ZPE",1);
	self.lastEnergy = 0;
	self.rate = 0;
	self:CreateWireOutputs("Active","Overload","Energy");
	local phys = self:GetPhysicsObject();
	if(phys:IsValid()) then
		phys:Wake();
		phys:SetMass(1000);
	end
end

--################## Spawn the SENT @JDM12989
function ENT:SpawnFunction(p,t)
	if(not t.Hit) then return end;
	local e = ents.Create("zpm_device");
	e:SetPos(t.HitPos+Vector(0,0,50));
	e:Spawn();
	local ang = p:GetAimVector():Angle(); ang.p = -90; ang.r = 0; ang.y = 0;
	e:SetAngles(ang)
	return e;
end

--################## OnRemove @JDM12989
function ENT:OnRemove()
	if (StarGate.HasResourceDistribution) then
		Dev_Unlink_All(self);
	end
	self.alarm:Stop();
end

--################## Adds the annoying overlay speechbubble to this SENT @JDM12989
function ENT:ShowOutput(energy)
	local add = "(Off)";
	if (self.enabled) then add = "(On)" end;
	self:SetOverlayText("ZPM Device "..add.."\nEnergy = "..energy);
end

--################## Shows console @JDM12989
function ENT:Use(ply)
	umsg.Start("Show_ZPD_Console",ply);
		umsg.Entity(self);
	umsg.End();
	ply.ZPMDevice = self;
end

--################## Turn On code for gui button @JDM12989
function TurnOnCallBack(ply)
	local dev = ply.ZPMDevice;
	local d = 12; -- distance or downward motion, also used for weld timing
	
	for i=1,3 do
		if (ValidEntity(dev.zpm_slot[i])) then
			dev.zpm_slot[i]:SetMoveType(MOVETYPE_NOCLIP);
		end
	end
	
	for h=1,d do
		timer.Simple((h-1)*0.1,
			function()
				for i=1,3 do
					if (ValidEntity(dev.zpm_slot[i])) then
						local ang = dev:GetAngles(); ang.p = ang.p -90;
						dev.zpm_slot[i]:SetAngles(ang);
						dev.zpm_slot[i]:SetPos(dev:LocalToWorld(dev.slot_cent[i]-Vector(h/2,0,0)));
					end
				end
			end
		);
	end
	
	timer.Simple(d/10,
		function()
			for i=1,3 do
				if (ValidEntity(dev.zpm_slot[i])) then
					constraint.RemoveConstraints(dev.zpm_slot[i],"Weld");
					constraint.NoCollide(dev,dev.zpm_slot[i],0,0);
					if (constraint.CanConstrain(dev,0)) then
						--Msg("yay dev\n");
					end
					if (constraint.CanConstrain(dev.zpm_slot[i],0)) then
						--Msg("yay zpm"..i.."\n");
					end
					constraint.Weld(dev,dev.zpm_slot[i],0,dev.zpm_slot[i].PhysicsBone,0);
					dev.zpm_slot[i]:SetMoveType(MOVETYPE_VPHYSICS);
					if (StarGate.HasResourceDistribution) then
						Dev_Link(dev.zpm_slot[i],dev,dev.zpm_slot[i]:GetPos(),dev:GetPos(),"cable/cable2",Color(0,0,0,0),0);
					end
				end
			end
			dev.enabled = true;
		end
	);
end
concommand.Add("TurnOn",TurnOnCallBack);

--################## Turn Off code for gui button @JDM12989
function TurnOffCallBack(ply)
	local dev = ply.ZPMDevice;
	dev.enabled = false;
	for i=1,3 do
		if (ValidEntity(dev.zpm_slot[i])) then
			local ang = dev:GetAngles(); ang.p = ang.p -90;
			dev.zpm_slot[i]:SetAngles(ang);
			dev.zpm_slot[i]:SetMoveType(MOVETYPE_NOCLIP);
			constraint.RemoveConstraints(dev.zpm_slot[i],"Weld");
			dev.zpm_slot[i]:SetPos(dev:LocalToWorld(dev.slot_cent[i]));
			constraint.Weld(dev,dev.zpm_slot[i],0,0,1000,true);
			dev.zpm_slot[i]:SetMoveType(MOVETYPE_VPHYSICS);
			if (ValidEntity(dev.zpm_slot[i]) and StarGate.HasResourceDistribution) then
				Dev_Unlink(dev.zpm_slot[i],dev);
			end
		end
	end
end
concommand.Add("TurnOff",TurnOffCallBack);

--################## Toggles Overload @JDM12989
function OverloadCallBack(ply,cmd,args)
	local dev = ply.ZPMDevice;
	if (dev.overload) then
		dev.overload = false;
		dev.alarm:Stop();
	else
		dev.overload = true;
		dev.alarm:Play();
	end
end
concommand.Add("Overload",OverloadCallBack);   

--################## Think @JDM12989
function ENT:Think(ply)
	if not (StarGate.HasResourceDistribution) then return end;
	local energy = self:GetResource("energy");
	local ZPE = self:GetResource("ZPE");
	local nw_capacity = self:GetNetworkCapacity("energy");
	
	self:ZPMUpdate(ply);
	self:ShowOutput(energy);
	local none, slotCenter, local_ents, zpm;
	
	-- check whether the device is on or not
	-- run energy convertion at needed rate or zpm attachment
	if (self.enabled) then
		local diff = self.lastEnergy - energy;
		self.rate = self.rate + diff;
		if (energy < nw_capacity) then
			self.rate = self.rate + 1000;
		else
			self.rate = 0;
		end
		if (ZPE >= self.rate) then
			self:SupplyResource("energy",self.rate);
			self:ConsumeResource("ZPE",self.rate);
		else
			self:SupplyResource("energy",ZPE);
			self:ConsumeResource("ZPE",ZPE);
		end
		self.lastEnergy = energy;
	else
		for i=1,3 do
			none = true;
			slotCenter = self:LocalToWorld(self.slot_cent[i]);
			local_ents = ents.FindInSphere(slotCenter,10);
			for _,v in pairs(local_ents) do
				if (v:IsValid() and v:GetClass() == "zpm") then
					none = false;
					zpm = v;
				end
			end
			if (none) then
				self.zpm_slot[i] = nil;
			else
				self:AttachZPM(zpm,i);
			end
		end
	end

	-- check if device is on and set to overload
	-- FIX ME! 
	if (self.enabled and self.overload) then
		for i=1,3 do
			local zpm = self.zpm_slot[i];
			if (zpm ~= nil) then
				local zpe = zpm:GetResource("ZPE");
				if (zpe >= 100000) then
					zpm:ConsumeResource("ZPE",100000);
					self.buffer = self.buffer + 100000;
				else
					zpm:ConsumeResource("ZPE",zpe);
					self.buffer = self.buffer + zpe;
				end
			end
		end
		-- BOOM!?
		if (self.buffer > self.buffer_max) then
			--[[ explosion effects!!! I HAVE ALMOST NO IDEA WHAT I'M DOING HERE!
			local fx = EffectData();
			fx:SetOrigin(self:GetPos());
			fx:SetNormal(self);
			fx:SetEntity(util.TraceLine({start=self:GetPos(),endpos=self:GetPos(),filter={self,self:GetOwner()}}));
			fx:SetScale(-1);
			fx:SetAngle(Angle(255,255,255));
			util.Effect("staff_explosion",fx,true,true);
			-- damage
			local attacker,owner = StarGate.GetAttackerAndOwner(self);
			StarGate.BlastDamage(attacker,owner,self:GetPos(),self.radius,self.damage);]]
			for i=1,3 do
				if (self.zpm_slot[i] ~= nil) then
					self.zpm_slot[i]:Remove();
				end
			end
			self:Remove();
		end
	else
		self.buffer = math.Clamp((self.buffer - 100000),0,self.buffer_max);
	end

	self:SetWire("Active",self.enabled);
	self:SetWire("Overload",self.overload);
	self:SetWire("Energy",energy);
	
	self:NextThink(CurTime()+1);
	return true;
end

--################## positions and welds zpm into place @JDM12989
function ENT:AttachZPM(zpm,i)
	-- position ZPM
	local phys = zpm:GetPhysicsObject();
	phys:EnableMotion(true);
	local ang = self:GetAngles(); ang.p = ang.p -90;
	zpm:SetAngles(ang);
	zpm:SetPos(self:LocalToWorld(self.slot_cent[i]));
	self.Weld = constraint.Weld(self,zpm,0,0,1000,true,true);
	phys:Wake();
	self.zpm_slot[i] = zpm;
end

--################## update all values in the gui @JDM12989
function ENT:ZPMUpdate(ply)
	local zpme = {0,0,0};
	
	for i=1,3 do
		if (self.zpm_slot[i] ~= nil) then
			zpme[i] = (self.zpm_slot[i]:GetResource("ZPE")/self.zpm_slot[i].MaxEnergy)*100;
		else
			zpme[i] = -1;
		end
	end
	
	umsg.Start("ZPD_Update",ply);
		umsg.Entity(self);
		umsg.Bool(self.enabled);
		umsg.Short(zpme[1]);
		umsg.Short(zpme[2]);
		umsg.Short(zpme[3]);
		umsg.Short((self.buffer/self.buffer_max)*100);
	umsg.End();
end
